#pragma once
#include <unordered_map>
#include <unordered_set>
#include <string_view>
#include <functional>
#include <filesystem>
#include <fstream>
#include <thread>
#include <string>

#include "database.hpp"
#include "socket.hpp"

namespace http
{
    constexpr size_t BUFFER_LENGTH = 4096;

    struct buffer
    {
        class iterator
        {
        public:
            iterator(const buffer& buffer) noexcept;
            iterator(const buffer& buffer, char* data) noexcept;
            iterator(const iterator& it) noexcept;

            iterator& operator=(const iterator& it);
            iterator& operator++() noexcept;
            iterator& operator--() noexcept;
            iterator operator++(int) noexcept;
            iterator operator--(int) noexcept;
            iterator operator+(size_t pos) const noexcept;
            iterator operator-(size_t pos) const noexcept;
            size_t operator-(const iterator& it) const noexcept;
            iterator& operator+=(size_t pos) noexcept;
            iterator& operator-=(size_t pos) noexcept;
            char& operator*();
            char* operator->();

            bool operator<(const iterator& other) const;
            bool operator>(const iterator& other) const;
            bool operator==(const iterator& other) const;
            bool operator<=(const iterator& other) const;
            bool operator>=(const iterator& other) const;
            bool operator!=(const iterator& other) const;
        private:
            void add(size_t pos);
            void sub(size_t pos);
        private:
            const buffer& _buf;
            char* _data;
        };

        buffer();
        ~buffer();

        void reset() noexcept;
        size_t getNextContinueUninitializedSpaceSize();
        void movePtr(char*& ptr, size_t pos) noexcept;
        iterator begin() noexcept;
        iterator end() noexcept;
        char* _buffer_begin;
        char* _buffer_end;
        char* _data_begin;
        char* _data_end;
    };

    class http_res
    {
    private:
        struct http_head
        {
            std::string _version;
            short _code;
            std::string _status;
        };

        struct http_property
        {
            std::unordered_map<std::string, std::string> _data;
        };

        struct http_content
        {
            std::string _data;
        };
    public:
        std::string serialize();
    public:
        http_head _head;
        http_property _property;
        http_content _content;
    };

    class http_req
    {
    private:
        struct http_head
        {
            http_head() noexcept
                : _is_filled(false)
            {}

            std::string _method;
            std::string _url;
            std::string _version;
            bool _is_filled;
        };

        struct http_property
        {
            http_property() noexcept
                : _is_filled(false)
            {}
        
            std::unordered_map<std::string, std::string> _data;
            bool _is_filled;
        };

        struct http_content
        {
            http_content() noexcept
                : _is_filled(false)
            {}

            std::string _data;
            bool _is_filled;
        };
    public:
        bool isComplated();
        void clear();
        void deSerialize(buffer& buf);
    public:
        http_head _head;
        http_property _property;
        http_content _content;
    };

    class http_server
    {
    public:
        http_server(short port, const std::string& ip = "0.0.0.0");
        void start();
    private:
        void loop(int fd);
        http_res parse(http_req& request);
        void connection(int connection_fd, sockaddr_in client_msg, socklen_t client_msg_len);
    private:
        static const std::unordered_map<std::string_view, std::string_view> _mime_table;
        static const std::unordered_set<std::string_view> _ip_any;
    private:
        sockaddr_in _server_msg;
        socklen_t _server_msg_len;
    };
}